// ----------------------------------------------------------------------------
//
// OpenSteerDotNet - pure .net port
// Port by Simon Oliver - http://www.handcircus.com
//
// OpenSteer -- Steering Behaviors for Autonomous Characters
//
// Copyright (c) 2002-2003, Sony Computer Entertainment America
// Original author: Craig Reynolds <craig_reynolds@playstation.sony.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//
// ----------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace XDL.Framework.OpenSteer
{
    public interface AbstractTokenForProximityDatabase
    {
        /// <summary>
        /// Activate/deactivate a DB entry.
        /// </summary>
        bool Activated { get; set; }

        void updateForNewPosition (Vector3 position);
        // find all neighbors within the given sphere (as center and radius)
        void findNeighbors(Vector3 center, float radius, List<AbstractVehicle> results);
    }
    
    public interface AbstractProximityDatabase
    {    
        // type for the "tokens" manipulated by this spatial database
        //typedef AbstractTokenForProximityDatabase<ContentType> tokenType;

        // allocate a token to represent a given client object in this database
        AbstractTokenForProximityDatabase allocateToken(AbstractVehicle parentObject);

        // returns the number of tokens in the proximity database
        int getPopulation();

        AbstractVehicle getNearestVehicle(Vector3 position, float radius);

        Vector3 getMostPopulatedBinCenter();
    }


    public class BruteForceProximityDatabase : 
        AbstractProximityDatabase,
        IEnumerable<tokenType>
    {
        // list containing all tokens in database
        LinkedList<tokenType> group;

        // constructor
        public BruteForceProximityDatabase()
        {
            group = new LinkedList<tokenType>();
        }

        // allocate a token to represent a given client object in this database
        public AbstractTokenForProximityDatabase allocateToken(AbstractVehicle parentObject)
        {
            tokenType tToken = new tokenType(parentObject, this);
            return (AbstractTokenForProximityDatabase)tToken;
        }

        public LinkedListNode<tokenType> Add(AbstractTokenForProximityDatabase token)
        {
            return group.AddLast((tokenType)token);
        }

        public void Remove(AbstractTokenForProximityDatabase token)
        {
            group.Remove(((tokenType)token).Node);
        }

        // return the number of tokens currently in the database
        public int getPopulation()
        {
            return group.Count;//.size();
        }

        public AbstractVehicle getNearestVehicle(Vector3 position, float radius)
        {
            return null;
        }

        public Vector3 getMostPopulatedBinCenter()
        {
            return Vector3.Zero;
        }

        #region IEnumerable<tokenType> Members

        IEnumerator<tokenType> IEnumerable<tokenType>.GetEnumerator()
        {
            return group.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        public System.Collections.IEnumerator GetEnumerator()
        {
            return group.GetEnumerator();
        }

        #endregion
    }

    // "token" to represent objects stored in the database
    public class tokenType : AbstractTokenForProximityDatabase
    {
        BruteForceProximityDatabase m_bfpd;
        AbstractVehicle m_parentObject;
        Vector3 m_position;
        public LinkedListNode<tokenType> Node;

        // constructor
        public tokenType(AbstractVehicle parentObject, BruteForceProximityDatabase pd)
        {
            // store pointer to our associated database and the object this
            // token represents, and store this token on the database's vector
            m_bfpd = pd;
            m_parentObject = parentObject;
            Node = m_bfpd.Add(this);
        }

        /// <summary>
        /// Activate/deactivate a DB entry.
        /// </summary>
        public bool Activated 
        {
            get
            {
                return Node != null;
            }

            set
            {
                if (value == true && Node == null )
                {
                    Node = m_bfpd.Add(this);
                }
                else if (value == false && Node != null)
                {
                    m_bfpd.Remove(this);
                    Node = null;
                }
            }
        }

        // the client object calls this each time its position changes
        public void updateForNewPosition(Vector3 newPosition)
        {
            m_position = newPosition;
        }

        // find all neighbors within the given sphere (as center and radius)
        public void findNeighbors(Vector3 center, float radius, List<AbstractVehicle> results)
        {
            // loop over all tokens
            float r2 = radius * radius;
            
            //bfpd.group.ForEach(delegate(tokenType tToken)
            foreach(tokenType tToken in m_bfpd)
            {
                //for (tokenIterator i = bfpd->group.begin(); i != bfpd->group.end(); i++)
                //{
                float d2 = 0;
                Vector3.DistanceSquared(ref center, ref tToken.m_position, out d2);
                // push onto result vector when within given radius
                if (d2 < r2) 
                    results.Add(tToken.m_parentObject);//.push_back ((**i).object);
            }
        }
    }
}
